using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.Practices.Unity.Interception
{
    [TestClass]
    public class OutParametersInterfaceInterceptorTest
    {
        static TInterface WrapAndCreateType<TInterface, TConcrete>(IEnumerable<KeyValuePair<MethodBase, List<IInterceptionHandler>>> handlers)
            where TConcrete : TInterface
        {
            Type wrappedType = InterfaceInterceptor.WrapInterface(typeof(TInterface));
            ILEmitProxy proxy = new ILEmitProxy(handlers);
            object target = Activator.CreateInstance(typeof(TConcrete));
            List<object> wrappedCtorArgs = new List<object> { proxy, target };
            return (TInterface)Activator.CreateInstance(wrappedType, wrappedCtorArgs.ToArray());
        }

        [TestMethod]
        public void OutComplexValueType()
        {
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyOut).GetMethod("OutComplexValueType");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyOut result = WrapAndCreateType<ISpyOut, SpyOut>(dictionary);
            ComplexValueType outValue;
            result.OutComplexValueType(out outValue);

            Assert.AreEqual(byte.MaxValue, outValue.Byte);
            Assert.AreEqual('a', outValue.Char);
            Assert.AreEqual(decimal.MaxValue, outValue.Decimal);
            Assert.AreEqual(double.MaxValue, outValue.Double);
            Assert.AreEqual(float.MaxValue, outValue.Float);
            Assert.AreEqual(int.MaxValue, outValue.Int);
            Assert.AreEqual(long.MaxValue, outValue.Long);
            Assert.AreEqual(short.MaxValue, outValue.Short);
            Assert.AreEqual("Hello, world!", outValue.String);
            Assert.AreEqual(uint.MaxValue, outValue.UInt);
            Assert.AreEqual(ulong.MaxValue, outValue.ULong);
            Assert.AreEqual(ushort.MaxValue, outValue.UShort);
        }

        [TestMethod]
        public void OutDouble()
        {
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyOut).GetMethod("OutDouble");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyOut result = WrapAndCreateType<ISpyOut, SpyOut>(dictionary);
            double outValue;
            result.OutDouble(out outValue);

            Assert.AreEqual(double.MaxValue, outValue);
        }

        [TestMethod]
        public void OutInt16()
        {
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyOut).GetMethod("OutInt16");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyOut result = WrapAndCreateType<ISpyOut, SpyOut>(dictionary);
            short outValue;
            result.OutInt16(out outValue);

            Assert.AreEqual(short.MaxValue, outValue);
        }

        [TestMethod]
        public void OutInt32()
        {
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyOut).GetMethod("OutInt32");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyOut result = WrapAndCreateType<ISpyOut, SpyOut>(dictionary);
            int outValue;
            result.OutInt32(out outValue);

            Assert.AreEqual(int.MaxValue, outValue);
        }

        [TestMethod]
        public void OutInt64()
        {
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyOut).GetMethod("OutInt64");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyOut result = WrapAndCreateType<ISpyOut, SpyOut>(dictionary);
            long outValue;
            result.OutInt64(out outValue);

            Assert.AreEqual(long.MaxValue, outValue);
        }

        [TestMethod]
        public void OutReferenceType()
        {
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyOut).GetMethod("OutReferenceType");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyOut result = WrapAndCreateType<ISpyOut, SpyOut>(dictionary);
            string outReference;
            result.OutReferenceType(out outReference);

            Assert.AreEqual("Hello, world!", outReference);
        }

        public interface ISpyOut
        {
            void OutChar(out char outValue);
            void OutComplexValueType(out ComplexValueType outValueType);
            void OutDouble(out double outValue);
            void OutInt16(out short outValue);
            void OutInt32(out int outValue);
            void OutInt64(out long outValue);
            void OutReferenceType(out string outReference);
        }

        sealed class SpyOut : ISpyOut
        {
            public void OutChar(out char outValue)
            {
                outValue = 'a';
            }

            public void OutComplexValueType(out ComplexValueType outValueType)
            {
                outValueType = new ComplexValueType { 
                                                        Byte = byte.MaxValue, Char = 'a', 
                                                        Decimal = decimal.MaxValue, 
                                                        Double = double.MaxValue, 
                                                        Float = float.MaxValue, 
                                                        Int = int.MaxValue, 
                                                        Long = long.MaxValue, 
                                                        Short = short.MaxValue, 
                                                        String = "Hello, world!", 
                                                        UInt = uint.MaxValue, 
                                                        ULong = ulong.MaxValue, 
                                                        UShort = ushort.MaxValue };
            }

            public void OutDouble(out double outValue)
            {
                outValue = double.MaxValue;
            }

            public void OutInt16(out short outValue)
            {
                outValue = short.MaxValue;
            }

            public void OutInt32(out int outValue)
            {
                outValue = int.MaxValue;
            }

            public void OutInt64(out long outValue)
            {
                outValue = long.MaxValue;
            }

            public void OutReferenceType(out string outReference)
            {
                outReference = "Hello, world!";
            }
        }
    }
}